﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows;
using Bling.DSL;
using Bling.Properties;
using Bling.Util;
using Bling.Animation;
using System.Windows.Threading;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.IO;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using linq = Microsoft.Linq.Expressions;
using System.Linq;

namespace Bling.Physics.Gen {
  public interface CodeDriver {
    void Background();
  }
  internal class CodeGenerator : Eval<CodeGenerator>, PhysicsEval<CodeGenerator>, TableEval<CodeGenerator> {
    private StringWriter Current = null;
    internal World World;
    internal double DeltaTime0;

    private readonly Dictionary<BaseExpr, int> Temps = new Dictionary<BaseExpr, int>();
    private int TempIndex = 6;
    private readonly PhysicsEvalHelper<CodeGenerator> Physics0;
    public PhysicsEvalHelper<CodeGenerator> Physics { get { return Physics0; } }
    public Info<T> Access<T>(string name, Info<int> idx) {
      return new MyInfo<T>() { Text = name + (idx == null ? "" : "[" + ((MyInfo<int>)idx).Text + "]") };
    }
    public Info<T> PropertyValue<T>(DependencyPropertyValue<T> value) { return Physics.Eval(value); }
    public Info<double> DeltaTime() { return new MyInfo<double>() { Text = DeltaTime0.ToString() }; }


    private string id(int n) {
      string s = "";
      for (int i = 0; i < n; i++) s += " ";
      return s;
    }
    private void WriteLine(int ind, string text) {
      if (Current != null) 
        Current.WriteLine(id(ind) + text);
    }
    private string MakeTemp(BaseExpr value, Func<string> compute) {
      if (Temps.ContainsKey(value)) return "t" + Temps[value];
      var Index = Temps.Count;
      Temps[value] = Index;
      WriteLine(TempIndex, value.TypeOfT.Name + " t" + Index + " = " + compute() + ";"); // + " /* " + value + " */");
      return "t" + Index;
    }
    private string MakeTempB(BaseExpr value, Action<string> compute) {
      if (Temps.ContainsKey(value)) return "t" + Temps[value];
      var Index = Temps.Count;
      Temps[value] = Index;
      WriteLine(TempIndex, value.TypeOfT.Name + " t" + Index + ";");
      compute("t" + Index);
      return "t" + Index;
    }
    public Info<T> Block<T>(T[] Values, Info<int> idx) { throw new NotSupportedException(); }

    public Info<T> Table<T>(TableExpr<T> value) {
      return new MyInfo<T>() {
        Text = MakeTempB(value, (tid) => {
          var index = ((MyInfo<int>) value.Index.Eval(this)).Text;
          var results = new string[value.Values.Length];
          for (int i = 0; i < value.Values.Length; i++) {
            results[i] = ((MyInfo<T>) value.Values[i].Eval(this)).Text;
          }
          WriteLine(TempIndex, "switch (" + index + ") { /* " + value.Index + " */");
          for (int i = 0; i < value.Values.Length; i++) {
            WriteLine(TempIndex + 2, "case " + i + ": " + tid + " = " + results[i] + "; break; /* " + value.Values[i] + " */");
          }
          WriteLine(TempIndex + 2, "default: throw new Exception();"); // kill the path. 
          WriteLine(TempIndex, "}");
        })
      };
    }
    private abstract class BaseInfo {
      public string Text;
    }
    private class MyInfo<T> : BaseInfo, Info<T> {}

    public override Eval<CodeGenerator>.Info<S> UpCast<T, S>(Eval<CodeGenerator>.Info<T> info) {
      return new MyInfo<S>() { Text = ((MyInfo<T>)info).Text };
    }

    public string NameFor<T>(BasePhysicsPropertyValue<T> value, bool isScratch) {
      return isScratch ? Physics.Names.ScratchFor(value) : Physics.Names.NameFor(value);
    }
    public override Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value) {
      if (typeof(T) == typeof(double)) return new MyInfo<T>() { Text = ((MyInfo<double>)value.Values[0].Eval(this)).Text };

      var text = "(";
      for (int i = 0; i < value.Values.Count; i++) {
        if (i != 0) text = text + ", ";
        text = text + ((MyInfo<double>)value.Values[i].Eval(this)).Text;
      }
      text += ")";
      string Prefix;
      if (typeof(T) == typeof(Color)) Prefix = "Color.FromScRgb";
      else Prefix = "new " + typeof(T).Name;
      return new MyInfo<T>() { Text = Prefix + text };
    }
    public override Info<T> Constant<T>(Constant<T> constant) {
      if (typeof(T) == typeof(Point)) {
        var p = (Point)(object)constant.Value;
        return new MyInfo<T>() { Text = "(new " + typeof(T).FullName + "(" + p.X + ", " + p.Y + "))" };
      }
      if (typeof(T) == typeof(Point3D)) {
        var p = (Point3D)(object)constant.Value;
        return new MyInfo<T>() { Text = "(new " + typeof(T).FullName + "(" + p.X + ", " + p.Y + "," + p.Z + "))" };
      }
      if (typeof(T) == typeof(Color)) {
        var p = (Color)(object)constant.Value;
        return new MyInfo<T>() { Text = typeof(T).FullName + ".FromScRGB(" + p.ScA + ", " + p.ScR + "," + p.ScG + "," + p.ScB + "))" };
        //return (Info<T>)(object)ColorBl.FromScRGB(p.ScA, p.ScR, p.ScG, p.ScB).Underlying.Eval(this);
      }
      if (typeof(T) == typeof(bool)) {
        return new MyInfo<T>() {
          Text = ((bool)(object)constant.Value) ? "true" : "false"
        };
      }
      var text = constant.Value.ToString();
      if (typeof(T) == typeof(double)) {
        double d = (double)(object)constant.Value;
        if (d == double.PositiveInfinity) text = "double.PositiveInfinity";
        else if (d == double.NegativeInfinity) text = "double.NegativeInfinity";
        else if (d == double.MaxValue) text = "double.MaxValue";
        else if (d == double.MinValue) text = "double.MinValue";
        else if (d == double.NaN) text = "double.NaN";
        else if (d == double.Epsilon) text = "double.Epsilon";
        else text = text + "d";
      }
      return new MyInfo<T>() {
        Text = text
      };
    }
    public override Info<T> Operate<S, T>(Operation<S, T> op) {
      return new MyInfo<T>() {
        Text = MakeTemp(op, () => {
          var a = ((MyInfo<S>)op.Value.Eval(this)).Text;
          return op.Op.AsText(a);
        })
      };
    }
    public override Info<U> Operate<S, T, U>(Operation<S, T, U> op) {
      return new MyInfo<U>() {
        Text = MakeTemp(op, () => {
          var a1 = ((MyInfo<S>)op.Value1.Eval(this)).Text;
          var a2 = ((MyInfo<T>)op.Value2.Eval(this)).Text;
          return op.Op.AsText(a1, a2);
        })
      };
    }
    public override Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op) {
      return new MyInfo<V>() {
        Text = MakeTemp(op, () => {
          var a1 = ((MyInfo<S>)op.Value1.Eval(this)).Text;
          var a2 = ((MyInfo<T>)op.Value2.Eval(this)).Text;
          var a3 = ((MyInfo<U>)op.Value3.Eval(this)).Text;
          return op.Op.Marker.Trinary0(a1, a2, a3);
        })
      };
    }
    private string typeString(Type t) {
      return t.Namespace + "." + t.Name;
    }
    private void FillConstraints() {
      for (int g = 0; g < World.Constraints.Count; g++) {
        var constraint = World.Constraints[g];
        Temps.Clear();
        Physics.Indices.Clear();
        WriteLine(4, "{");
        var loop = ((Expr) constraint.BaseLHS).FindLoop;

        if (loop != null) {
          (constraint.BaseLHS.CurrentIndex == loop || loop.Parent == null).Assert();
          (constraint.BaseLHS.BaseContainer.Block.CurrentIndex == (loop.Parent == null ? loop : loop.Parent)).Assert();
          (loop.Parent == null || loop.Parent.Parent == null).Assert();

          WriteLine(6, "" + typeString(constraint.BaseLHS.TypeOfT) + "[] Temp = " + Physics.Names.ScratchFor(constraint.BaseLHS) + ";");
          WriteLine(6, "int k = 0;");
          WriteLine(6, "for (int i = 0; i < " + constraint.BaseLHS.BaseContainer.Block.Count + "; i++) {");
          WriteLine(8, "for (int j = 0; j < " + constraint.BaseLHS.BaseCount + "; j++) {");
          {
            Physics.Loop(((CurrentIndex)constraint.BaseLHS.BaseContainer.Index.Underlying),
                             new MyInfo<int>() { Text = "i" },
                             (constraint.BaseLHS.CurrentIndex),
                             new MyInfo<int>() { Text = "j" },
                             new MyInfo<int>() { Text = "k" });
            TempIndex = 10;
            WriteLine(10, "Temp[k] = " + ((BaseInfo)constraint.BaseRHS.BaseEval(this)).Text + "; /* " + constraint.BaseRHS + " */");
            if (constraint.BaseLHS.TypeOfT == typeof(double) ||
                constraint.BaseLHS.TypeOfT == typeof(Point) ||
                constraint.BaseLHS.TypeOfT == typeof(Point3D))
              WriteLine(10, "if (Temp[k].IsNan()) throw new Exception();");

          }
          WriteLine(10, "k = k + 1;");
          WriteLine(8, "}");
          WriteLine(6, "}");

          if (World.HasStepB.Contains(g)) { // copy out the old 
            WriteLine(6, "" + Physics.Names.ScratchFor(constraint.BaseLHS    ) + " = " + Physics.Names.NameFor(constraint.BaseLHS.Old) + ";");
            WriteLine(6, "" + Physics.Names.NameFor   (constraint.BaseLHS.Old) + " = " + Physics.Names.NameFor(constraint.BaseLHS    ) + ";");
          } else {
            WriteLine(6, "" + Physics.Names.ScratchFor(constraint.BaseLHS) + " = " + Physics.Names.NameFor(constraint.BaseLHS) + ";");
          }
          WriteLine(6, "" + Physics.Names.NameFor(constraint.BaseLHS) + " = Temp;");
        } else { // non-block, direct assignment. 


          // still...
          TempIndex = 6;
          // we don't need to use scratch. 
          WriteLine(6, typeString(constraint.BaseLHS.TypeOfT) + " temp = " + 
                        ((BaseInfo)constraint.BaseRHS.BaseEval(this)).Text + ";");

          var target = ((BaseInfo)((BaseExpr)constraint.BaseLHS).BaseEval(this)).Text;
          if (World.HasStepB.Contains(g)) {
            WriteLine(6, "" + Physics.Names.NameFor(constraint.BaseLHS.Old) + "[0] = " + Physics.Names.NameFor(constraint.BaseLHS) + "[0];");
          }
          WriteLine(6, target + " = temp;");
          if (constraint.BaseLHS.TypeOfT == typeof(double) ||
              constraint.BaseLHS.TypeOfT == typeof(Point) ||
              constraint.BaseLHS.TypeOfT == typeof(Point3D))
            WriteLine(6, "if (" + target + ".IsNan()) throw new Exception();");
        }
        WriteLine(4, "}");
      }
    }
    private void File(StringWriter ConstraintOut) {
      WriteLine(0, "using Bling.Util;");
      WriteLine(0, "using Bling.DSL;");
      WriteLine(0, "using Bling.Physics;");
      WriteLine(0, "using System;");
      WriteLine(0, "using System.Windows;");
      WriteLine(0, "using System.Windows.Media;");
      WriteLine(0, "using System.Windows.Media.Media3D;");

      WriteLine(0, "namespace " + World.Name + " {");
      WriteLine(2, "public class Backend : " +
          typeof(CodeDriver).Namespace + "." +
          typeof(CodeDriver).Name + " {");
      WriteLine(4, "public void Background() {");
      Current.Write(ConstraintOut.ToString());
      WriteLine(4, "}");
      // output all arrays
      foreach (var r in Physics.Names.Referenced) {
        if (r.Key is PhysicsPropertyValue) {
          var value = (PhysicsPropertyValue)r.Key;
          WriteLine(4, "public " + typeString(r.Key.TypeOfT) + "[] " + Physics.Names.NameFor(value) + " = new " + typeString(r.Key.TypeOfT) + "[" + value.AllocationSize + "];");
          WriteLine(4, "public " + typeString(r.Key.TypeOfT) + "[] " + Physics.Names.ScratchFor(value) + " = new " + typeString(r.Key.TypeOfT) + "[" + value.AllocationSize + "];");
          if (World.HasStep.Contains(value)) {
            WriteLine(4, "public " + typeString(r.Key.TypeOfT) + "[] " + Physics.Names.NameFor(value.Old) + " = new " + typeString(r.Key.TypeOfT) + "[" + value.AllocationSize + "];");
          }
        } else if (r.Key is BaseBlockDependencyPropertyValue) {
          var value = (BaseBlockDependencyPropertyValue)r.Key;
          WriteLine(4, "public " + typeString(r.Key.TypeOfT) + "[] " + Physics.Names.NameFor(value) + " = new " + typeString(r.Key.TypeOfT) + "[" + value.AllocationSize + "];");
        } else if (r.Key is DependencyPropertyValue) {
          var value = (DependencyPropertyValue)r.Key;
          WriteLine(4, "public " + typeString(r.Key.TypeOfT) + " " + Physics.Names.NameFor(value) + ";");
        } else throw new NotSupportedException();
      }
      WriteLine(2, "}");
      WriteLine(0, "}");
    }
    private Type Compile(StringWriter BodyOut) {
      var provider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
      var parameters = new CompilerParameters() {
        GenerateExecutable = false,
        //GenerateInMemory = true,
        OutputAssembly = World.Name + ".dll",
        IncludeDebugInformation = true
      };
      foreach (var a in this.GetType().Assembly.GetReferencedAssemblies()) {
        parameters.ReferencedAssemblies.Add(a.Name + ".dll");
      }
      parameters.ReferencedAssemblies.Add(this.GetType().Assembly.GetName().Name + ".dll");
      // some reason must add manually.
      parameters.ReferencedAssemblies.Add("PresentationCore.dll");
      if (false) foreach (var a in parameters.ReferencedAssemblies) {
          Console.WriteLine("ASSEMBLY: " + a);
        }
      var textFile = System.IO.File.CreateText(World.Name + ".cs");
      var Code = BodyOut.ToString();
      textFile.Write(Code);
      textFile.Close();
      var result = provider.CompileAssemblyFromFile(parameters, World.Name + ".cs");
      provider.Dispose();
      if (result.Errors.Count != 0) {
        foreach (var e in result.Errors)
          Console.WriteLine(e);
        throw new Exception("There were compiler errors!");
      }
      var assembly = result.CompiledAssembly;
      var tpe = assembly.GetType(World.Name + ".Backend");
      return tpe;
    }
    public void CaptureRest() {
      foreach (var v in World.Links) {
        v.BasePhysics.Visit(new CaptureEval() { Parent = this });
      }
      foreach (var v in World.Inits) {
        v.BaseLHS.Visit(new CaptureEval() { Parent = this });
      }
    }
    private class CaptureEval : TranslateEval<CaptureEval>, ExprVisitor<int> {
      public CodeGenerator Parent;
      public override Expr<T> Translate<T>(Expr<T> value) {
        if (value is BasePhysicsPropertyValue<T>) {
          Parent.Physics.Eval(((BasePhysicsPropertyValue<T>)value).Normal);
          return value;
        } else if (value is DependencyPropertyValue<T>) {
          Parent.Physics.Eval(((DependencyPropertyValue<T>)value));
          return value;
        } else if (value is BaseBlockDependencyPropertyValue<T>) {
          Parent.Physics.Eval(((BaseBlockDependencyPropertyValue<T>)value).Normal);
          return value;
        }
        return base.Translate<T>(value);
      }
      public int Visit<T>(Expr<T> value) {
        Translate(value);
        return 0;
      }
    }

    public CodeGenerator(World World) {
      this.World = World;
      // initialize all blocks
      foreach (var b in World.Blocks) b.Init();

      Physics0 = new PhysicsEvalHelper<CodeGenerator>() { PhysicsEval = this };
    
      var ConstraintOut = Current = new StringWriter();
      FillConstraints(); // now we know what we need. 
      Current = null;
      CaptureRest();
      var BodyOut = Current = new StringWriter();
      File(ConstraintOut);
      this.DriverType = Compile(BodyOut);
      var cons = DriverType.GetConstructor(new Type[0]);
      this.Driver = (CodeDriver)cons.Invoke(new object[0]);

      var InitUI0 = (new MyLinqEval(this)).DoInits().Generate();
      this.InitUI = () => {
        InitUI0(this.Driver);
      };
      var UpdateUI0 = (new MyLinqEval(this)).DoUpdateUI().Generate();
      this.UpdateUI = () => {
        UpdateUI0(this.Driver);
        foreach (var v in World.UpdateInUI) v();
      };
      World.Nows = (value) => {
        return value.Visit(new MyNowVisitor() { Parent = this });
      };
    }
    private class MyNowVisitor : ExprVisitor<BaseExpr> {
      internal CodeGenerator Parent;
      public BaseExpr Visit<T>(Expr<T> value) {
        return new MyNowValue<T>() {
          Parent = Parent,
          Underlying = ((BasePhysicsPropertyValue<T>) value),
        };
      }
    }
    private class DriverThisValue : Expr {
      internal CodeGenerator Outer;
      public override Type TypeOfT {
        get { return Outer.DriverType; }
      }
      public override int GetHashCode() {
        return Outer.GetHashCode();
      }
      public override bool Equals(object obj) {
        if (obj is DriverThisValue) {
          return Outer == ((DriverThisValue)obj).Outer;
        }
        return base.Equals(obj);
      }
      public override bool Solve(Assign assign, BaseExpr value) {
        throw new NotImplementedException();
      }
      public override R Visit<R>(ExprVisitor<R> v) {
        throw new NotImplementedException();
      }
      public override object BaseEval<EVAL>(Eval<EVAL> txt) {
        throw new NotImplementedException();
      }
    }

    private class MyNowValue<T> : Expr<T>, AssignNowExpr<T> {
      internal BasePhysicsPropertyValue<T> Underlying;
      internal CodeGenerator Parent;

      public override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
        if (txt is PhysicsEval<EVAL>)
          return Underlying.Eval(txt);
        else if (txt is Linq.LinqEval<EVAL>) {
          var eval = new MyLinqEval(Parent);
          // except...reuse temps...
          eval.SetScope((Linq.LinqEval<EVAL>)txt);
          eval.DriverThis = eval.MakeTemp(new DriverThisValue() { Outer = Parent }, () => linq.Expression.ConvertChecked(linq.Expression.Constant(Parent.Driver), Parent.DriverType));

          eval.Parameters = ((Linq.LinqEval<EVAL>)txt).Parameters;
          var ret = ((MyLinqEval.MyInfo<T>) Underlying.Eval(eval)).Value;
          //eval.Instructions.Add(ret);
          //var block = eval.PopScope();
          // add the driver
          /*
          var block = linq.Expression.Block(new linq.ParameterExpression[] { eval.DriverThis },
            linq.Expression.Block(
              linq.Expression.Assign(eval.DriverThis, 
                       linq.Expression.ConvertChecked(linq.Expression.Constant(Parent.Driver), Parent.DriverType)),
            ret));  
          */

          return new Linq.LinqEval<EVAL>.MyInfo<T>() { Value = ret };
        }

        throw new NotSupportedException();
      }
      private linq.Expression Core(MyLinqEval eval, Expr<T> value) {
        eval.DisablePhysics = true;
        var RHS0 = ((MyLinqEval.MyInfo<T>)value.Eval(eval)).Value;
        eval.DisablePhysics = false;
        var LHS0 = ((MyLinqEval.MyInfo<T>)Underlying.Eval(eval)).Value;
        var assign = linq.Expression.Assign(LHS0, RHS0);
        eval.Instructions.Add(assign);
        var action = eval.PopScope();

        action = linq.Expression.Block(new linq.ParameterExpression[] {
          eval.DriverThis
        }, linq.Expression.Block(
          linq.Expression.Assign(eval.DriverThis, linq.Expression.ConvertChecked(linq.Expression.Constant(Parent.Driver), Parent.DriverType)),
          action));
        return action; 
      }

      public Action SetNow(Expr<T> value) {
        var eval = new MyLinqEval(Parent);
        eval.Parameters = new Bling.Linq.ParameterContext();
        var action = Core(eval, value);
        return linq.Expression.Lambda<Action>(action, eval.Parameters.Flush()).Compile();
      }
      public Action<S> SetNow<S>(Expr<T> value, ParameterExpr<S> arg) {
        var eval = new MyLinqEval(Parent);
        eval.Parameters = new Bling.Linq.ParameterContext();
        eval.Parameters.Add(arg);
        var action = Core(eval, value);
        return linq.Expression.Lambda<Action<S>>(action, eval.Parameters.Flush()).Compile();
      }
      public Action<S,U> SetNow<S,U>(Expr<T> value, ParameterExpr<S> argA, ParameterExpr<U> argB) {
        var eval = new MyLinqEval(Parent);
        eval.Parameters = new Bling.Linq.ParameterContext();
        eval.Parameters.Add(argA);
        eval.Parameters.Add(argB);
        var action = Core(eval, value);
        return linq.Expression.Lambda<Action<S,U>>(action, eval.Parameters.Flush()).Compile();
      }
      public Action<S, U, V> SetNow<S, U, V>(Expr<T> value, ParameterExpr<S> argA, ParameterExpr<U> argB, ParameterExpr<V> argC) {
        var eval = new MyLinqEval(Parent);
        eval.Parameters = new Bling.Linq.ParameterContext();
        eval.Parameters.Add(argA);
        eval.Parameters.Add(argB);
        eval.Parameters.Add(argC);
        var action = Core(eval, value);
        return linq.Expression.Lambda<Action<S, U, V>>(action, eval.Parameters.Flush()).Compile();
      }
      public Action<S, U, V, W> SetNow<S, U, V, W>(Expr<T> value, ParameterExpr<S> argA, ParameterExpr<U> argB, ParameterExpr<V> argC, ParameterExpr<W> argD) {
        var eval = new MyLinqEval(Parent);
        eval.Parameters = new Bling.Linq.ParameterContext();
        eval.Parameters.Add(argA);
        eval.Parameters.Add(argB);
        eval.Parameters.Add(argC);
        eval.Parameters.Add(argD);
        var action = Core(eval, value);
        return linq.Expression.Lambda<Action<S, U, V, W>>(action, eval.Parameters.Flush()).Compile();
      }
      public bool SetNow(T value) {
        SetNow(new Constant<T>(value))();
        return true;
      }
    }

    private class MyLinqEval : Linq.LinqEval<MyLinqEval>, PhysicsEval<MyLinqEval> {
      public readonly CodeGenerator Parent;
      public readonly PhysicsEvalHelper<MyLinqEval> Physics0;
      private readonly List<linq.Expression> Assignments = new List<Microsoft.Linq.Expressions.Expression>();
      public bool DisablePhysics = false;

      public Info<double> DeltaTime() {
        return new MyInfo<double>() { Value = linq.Expression.Constant(Parent.DeltaTime0, typeof(double)) };
      }

      private class MyPhysicsEval : PhysicsEvalHelper<MyLinqEval> {
        public MyLinqEval Parent;
        public override Eval<MyLinqEval>.Info<T> Eval<T>(BaseBlockDependencyPropertyValue<T> value) {
          if (Parent.DisablePhysics || !Parent.Physics.Names.Referenced.ContainsKey(value.Normal)) {
            var obj = Parent.Access(value.BaseContainer);
            return new MyInfo<T>() {
              Value = linq.Expression.Convert(linq.Expression.Call(obj, "GetValue", new Type[] { },
                linq.Expression.Constant(value.ReadProperty)), typeof(T)),
            };
          } else return base.Eval<T>(value);
        }
      }
      private linq.Expression Access(PhysicsBlock.BaseBaseBlockValue value) {
        var block = (RetainedPhysicsBlock) value.Block;

        var idx = ((MyInfo<int>)value.Index.Underlying.Eval(this)).Value;
        var array = linq.Expression.Constant(block.BaseUnderlying, typeof(DependencyObject[]));
        var obj = linq.Expression.ArrayAccess(array, idx);
        return obj;
      }
      public Info<T> Access<T>(string name, Info<int> idx) {
        //if (DisablePhysics) throw new NotSupportedException();
        var fld = linq.Expression.Field(DriverThis, name);
        if (idx == null) {
          return new MyInfo<T>() { Value = fld };
        }
        return new MyInfo<T>() { Value = linq.Expression.ArrayAccess(fld, ((MyInfo<int>)idx).Value) };
      }
      public override Eval<MyLinqEval>.Info<T> PropertyValue<T>(DependencyPropertyValue<T> value) {
        if (DisablePhysics || 
          !Physics.Names.Referenced.ContainsKey(value)) return base.PropertyValue<T>(value);
        else return Physics.Eval(value);
      }
      public linq.Expression DoLoop(CurrentIndex LoopVar, Func<linq.Expression> doit, char name) {
        if (LoopVar == null) return doit();
        return DoLoop(LoopVar.Parent, () => {
          return For("" + name, LoopVar.Size, (pi) => {
            Physics.Indices[LoopVar] = pi;
            return doit();
          });
        }, (char)(name + 1));
      }
      public class MyPhysicsAssign : World.BaseLink.PhysicsAssign {
        public MyLinqEval Parent;
        public BoolBl Guard = true;
        public override void Process<S>(DependencyPropertyValue<S> LHS, Expr<S> RHS) {
          var LHS0 = ((MyInfo<S>)LHS.Eval(Parent)).Value;
          (!Parent.DisablePhysics).Assert();
          Parent.DisablePhysics = true;
          var RHS0 = ((MyInfo<S>)RHS.Eval(Parent)).Value;
          var Guard0 = ((MyInfo<bool>)Guard.Underlying.Eval(Parent)).Value;
          Parent.DisablePhysics = false;
          var assign = linq.Expression.Assign(LHS0, RHS0);
          Parent.Instructions.Add(linq.Expression.IfThen(Guard0, assign));
        }
        public override void Process<S>(BaseBlockDependencyPropertyValue<S> LHS, Expr<S> RHS) {
          Func<linq.Expression> doit = () => {
            var LHS0 = ((MyInfo<S>)LHS.Eval(Parent)).Value;
            (!Parent.DisablePhysics).Assert();
            Parent.DisablePhysics = true;
            var RHS0 = ((MyInfo<S>)RHS.Eval(Parent)).Value;
            var Guard0 = ((MyInfo<bool>)Guard.Underlying.Eval(Parent)).Value;
            Parent.DisablePhysics = false;
            var assign = linq.Expression.Assign(LHS0, RHS0);
            var cond = linq.Expression.IfThen(Guard0, assign);
            return cond;
          };
          Parent.Physics.Indices.Clear();
          Parent.Physics.SubBlockIndex = null;
          Parent.Physics.UseIndex = null;
          Parent.Physics.BodyIndex = null;
          var ins = Parent.DoLoop(LHS.FindLoop, doit, 'i');
          Parent.Instructions.Add(ins);
        }

        public override void Process<S>(BasePhysicsPropertyValue<S> LHS, Expr<S> RHS) {
          Func<linq.Expression> doit = () => {
            var LHS0 = ((MyInfo<S>)LHS.Eval(Parent)).Value;
            (!Parent.DisablePhysics).Assert();
            Parent.DisablePhysics = true;
            var RHS0 = ((MyInfo<S>)RHS.Eval(Parent)).Value;
            var Guard0 = ((MyInfo<bool>) Guard.Underlying.Eval(Parent)).Value;
            Parent.DisablePhysics = false;
            var assign = linq.Expression.Assign(LHS0, RHS0);
            var cond = linq.Expression.IfThen(Guard0, assign);
            return cond;
          };
          Parent.Physics.Indices.Clear();
          Parent.Physics.SubBlockIndex = null;
          Parent.Physics.UseIndex = null;
          Parent.Physics.BodyIndex = null;
          var ins = Parent.DoLoop(LHS.FindLoop, doit, 'i');
          Parent.Instructions.Add(ins);
        }
      }


      public class MyLinkAssign : World.BaseLink.LinkAssign {
        public MyLinqEval Parent;
        public BoolBl Guard = true;
        public override void Process<S>(DependencyPropertyValue<S> LHS, Expr<S> RHS) {
          Func<linq.Expression> doit = () => {
            var Target = linq.Expression.Constant(LHS.Target, typeof(DependencyObject));
            var Property = linq.Expression.Constant(LHS.WriteProperty, typeof(DependencyProperty));
            (!Parent.DisablePhysics).Assert();
            Parent.DisablePhysics = true;
            var Guard0 = ((MyInfo<bool>)Guard.Underlying.Eval(Parent)).Value;
            Parent.DisablePhysics = false;
            var RHS0 = ((MyInfo<S>)RHS.Eval(Parent)).Value;
            var assign = linq.Expression.Call(Target, "SetValue", new Type[] { }, Property, linq.Expression.Convert(RHS0, typeof(object)));
            var cond = linq.Expression.IfThen(Guard0, assign);
            return cond;
          };
          Parent.Physics.Indices.Clear();
          Parent.Physics.SubBlockIndex = null;
          Parent.Physics.UseIndex = null;
          Parent.Physics.BodyIndex = null;
          var ins = Parent.DoLoop(LHS.FindLoop, doit, 'i');
          Parent.Instructions.Add(ins);
        }
        public override void Process<S>(BaseBlockDependencyPropertyValue<S> LHS, Expr<S> RHS) {
          Func<linq.Expression> doit = () => {
            (!Parent.DisablePhysics).Assert();
            Parent.DisablePhysics = true;
            var Guard0 = ((MyInfo<bool>)Guard.Underlying.Eval(Parent)).Value;
            Parent.DisablePhysics = false;

            var RHS0 = ((MyInfo<S>)RHS.Eval(Parent)).Value;
            var obj = Parent.Access(LHS.BaseContainer);
            var Property = linq.Expression.Constant(LHS.WriteProperty, typeof(DependencyProperty));
            // the right dependency value
            var assign = linq.Expression.Call(obj, "SetValue", new Type[] { }, Property, linq.Expression.Convert(RHS0, typeof(object)));
            var cond = linq.Expression.IfThen(Guard0, assign);
            return cond;
          };
          Parent.Physics.Indices.Clear();
          Parent.Physics.SubBlockIndex = null;
          Parent.Physics.UseIndex = null;
          Parent.Physics.BodyIndex = null;
          var ins = Parent.DoLoop(LHS.FindLoop, doit, 'i');
          Parent.Instructions.Add(ins);
        }
      }
      public MyLinqEval DoInits() {
        foreach (var init in Parent.World.Inits) {
          PushScope();
          if (!init.Solve(new MyPhysicsAssign() { Parent = this }))
            throw new NotSupportedException();
          Assignments.Add(PopScope());
        }
        return this;
      }
      public MyLinqEval DoUpdateUI() {
        foreach (var reference in Physics.Names.Referenced) {
          if (reference.Key is BaseBlockDependencyPropertyValue) {
            PushScope();
            var LHS0 = (BaseBlockDependencyPropertyValue)reference.Key;
            // assign to myself. The other value will be assinged in the context of physics. 
            // (a) denormalize.
            LHS0 = LHS0.All;

            if (!LHS0.Solve(new MyPhysicsAssign() { Parent = this }, LHS0))
              throw new NotSupportedException();
            Assignments.Add(PopScope());
          } else if (reference.Key is DependencyPropertyValue) {
            PushScope();
            var LHS0 = (DependencyPropertyValue)reference.Key;
            // assign to myself. The other value will be assinged in the context of physics. 
            if (!LHS0.Solve(new MyPhysicsAssign() { Parent = this }, LHS0))
              throw new NotSupportedException();
            Assignments.Add(PopScope());
          }
        }
        foreach (var link in Parent.World.Links) {
          PushScope();
          Func<bool, bool> IsAlways = (b) => {
            if (link.UpdateLink is Constant<bool>) {
              var bA = ((Constant<bool>)link.UpdateLink).Value;
              return bA == b;
            }
            return false;
          };
          if (!IsAlways(false) && !link.BaseLinkValue.Solve(new MyLinkAssign() { Guard = link.UpdateLink, Parent = this }, link.BasePhysics))
            throw new NotSupportedException();
          if (!IsAlways(true) && !link.BasePhysics.Solve(new MyPhysicsAssign() {
            Parent = this, Guard = !link.UpdateLink.Bl(),
          }, link.BaseLinkValue)) {

            throw new NotSupportedException();
          }
          Assignments.Add(PopScope());
        }
        Parent.World.Links = null; // no more

        return this;
      }
      public MyLinqEval(CodeGenerator Parent) : base(null) {
        this.Parent = Parent;
        Physics0 = new MyPhysicsEval() { PhysicsEval = this, Parent = this, Names = Parent.Physics0.Names };
        DriverThis = linq.Expression.Parameter(Parent.DriverType, "driver");
        
      }
      public PhysicsEvalHelper<MyLinqEval> Physics { get { return Physics0; } }
      internal linq.ParameterExpression DriverThis;
      public Action<CodeDriver> Generate() {
        var RealParam = linq.Expression.Parameter(typeof(CodeDriver), "driver0");
        var assign = linq.Expression.Assign(DriverThis, linq.Expression.Convert(RealParam, Parent.DriverType));
        Assignments.Insert(0, assign);
        var block = linq.Expression.Block(new linq.ParameterExpression[] { DriverThis }, Assignments);
        return linq.Expression.Lambda<Action<CodeDriver>>(block, RealParam).Compile();
      }
      public Action<CodeDriver> GeneratePop() {
        Assignments.Add(PopScope());
        return Generate();
      }
    }
    public readonly Type DriverType;
    public readonly CodeDriver Driver;
    public readonly Action UpdateUI;
    public readonly Action InitUI;
  }
  // thats it?
  public class WorldDriver : CanStart {
    private readonly BackgroundTimer Timer;
    private Action InitUI;
    private readonly World World;
    public WorldDriver(World World, Dispatcher d, int millis) {
      this.World = World;
      CodeGenerator g = new CodeGenerator(World) { DeltaTime0 = millis };
      Action Background = () => {
        g.Driver.Background();
        foreach (var v in World.AfterRelax) v();
      };
      Action UpdateUI = g.UpdateUI;
      (g.InitUI != null).Assert();
      this.InitUI = g.InitUI;
      Timer = new BackgroundTimer(d, millis, Background, UpdateUI);
    }
    public void Start() {
      if (InitUI != null) {
        InitUI();
        foreach (var b in World.Blocks) b.Activate();
        InitUI = null;
      }
      Timer.Start(); 
    }
    public void Stop() { Timer.Stop(); }
  }
}